<?php
/**
 * Created by PhpStorm.
 * User: yzr
 * Date: 1/26/2021
 * Time: 16:36
 */
namespace app\common\model\job;


use app\common\model\base\A3Mall;
use app\common\model\file\Association;
use app\common\model\order\OrderTake;
use app\common\model\users\AccountLevelLimit;
use app\common\model\users\Wallet;

use app\common\model\users\WalletDetail;
use app\common\traits\recordAllRun;
use mall\basic\Users;
use think\facade\Db;

class Job extends A3Mall
{
    use recordAllRun;   // 新增链表查询设置

    protected $name = "job";

    /**
     * 关联文件类型
     */
    const FILE_TYPE = 'job';


    protected $type = [
        "job_no"=>"string",
        "type_id"=>"integer",
        "name"=>"string",
        "task_link"=>"string",
        "num"=>"integer",
        "accept_num"=>"integer",
        "accept_finish_num"=>"integer",
        "accept_fail_num"=>"integer",
        "accept_executing_num"=>"integer",
        "price"=>"float",
        "total_amount"=>"float",
        "exp_time"=>"integer",
        "publisher_id"=>"integer",
        "publish_time"=>"integer",
        "ver"   => 'integer',
        "status"   => 'integer',
        "update_time"   => 'integer',
    ];

    const STATUS_PENDING_APPROVAL = 11; //待审核状态（暂时发布为正常）
    const STATUS_DELETE = 0;    //取消订单
    const STATUS_EFFECTIVE = 1; // 正常有效订单
    const STATUS_ACCEPT_STOP = 2; // 停止接单（存在未完成接单）
    const STATUS_FINISH_NOT_ACCEPT = 3; // 完成未接单
    const STATUS_FINISH = 4;    //完成有接单

    //状态文本映射
    private static $status_text_mapping=[
        self::STATUS_DELETE=>'取消订单',
        self::STATUS_EFFECTIVE=>'正常有效订单',
        self::STATUS_ACCEPT_STOP=>'停止接单',
        self::STATUS_FINISH_NOT_ACCEPT=>'完成未接单',
        self::STATUS_FINISH=>'完成有接单',
        self::STATUS_PENDING_APPROVAL=>'待审核',

    ];

    /**
     * 获取可选择时长
     */
    public static function fetchDurationList() {
        $list = [
            [
                'key'   => 1,
                'value' => '1小时',
            ],
            [
                'key'   => 2,
                'value' => '2小时',
            ],
            [
                'key'   => 3,
                'value' => '3小时',
            ],
            [
                'key'   => 4,
                'value' => '4小时',
            ],
            [
                'key'   => 5,
                'value' => '5小时',
            ],
            [
                'key'   => 10,
                'value' => '10小时',
            ],
            [
                'key'   => 24,
                'value' => '24小时',
            ],
            [
                'key'   => 48,
                'value' => '48小时',
            ],
            [
                'key'   => 72,
                'value' => '72小时',
            ],
            [
                'key'   => 148,
                'value' => '148小时',
            ],
        ];
        return $list;
    }

    /**
     * 获取任务单价筛选范围
     */
    public static function fetchPriceScopeList() {
        $list = [
            '0~40',
            '40~400',
            '400~4000',
        ];
        return $list;
    }

    /**
     * 发布任务
     * @param $data
     */
    public function issue($data,$validateData) {

        // 添加发布任务
        $jobDetails = $this->issueRecord(array_merge($data,[
            'user_id'   => $validateData['user_id'],
            'time'  => $validateData['time'],
        ]));

        // 新增发布统计
        (new AccountLevelLimit())->issue($validateData['issueLimit'],$jobDetails['total_amount'],$jobDetails['publish_time']);

        // 冻结金额
        (new Wallet())->jobFrozen($validateData['wallet'],$jobDetails['id'],$jobDetails['total_amount']);

        return true;
    }

    /**
     * 编辑任务
     * @param $details 任务详情
     * @param $updateData 需要编辑的数据
     */
    public function edit($details,$data)
    {
        $updateData = [];
        $fields = ['type_id','duration','requirement','name','task_link'];
        foreach($data as $key=>$value) {
            switch($key) {
                case 'type_id':
                case 'requirement':
                case 'name':
                case 'task_link':
                    if($value != $details->$key) {
                        $updateData[$key] = $value;
                    }
                    break;
                case 'duration':
                    $updateData['exp_time'] = time() + 60 * 60 * $value;
                    break;
            }
        }
        if($updateData) {
            $updateData['update_time'] = time();
            if(!$this->where([
                'id'    => $details->id,
                'status'    => static::STATUS_EFFECTIVE,
            ])->update($updateData)) {
                throw new \Exception('数据发生变化，请刷新重试');
            }
        }


        if(array_key_exists('verify_imgs',$data)) {
            // 删除原来图片
            (new Association())->recordDeleteByJobId($details->id);
            // 添加新图片
            (new Association())->recordInsertListByJob($details->id,$data['verify_imgs']);
        }

        return true;

    }

    /**
     * 任务接单
     * @param $jobDetails 任务详情
     * @param $limitDetails 限制详情
     * @return bool
     * @throws \Exception
     */
    public function accept($jobDetails,$limitDetails){

        // 添加接单记录
        (new OrderTake())->accept($jobDetails);
        // 更新任务表
        $this->updateByIdAndVer($jobDetails['id'],$jobDetails['ver'],[
            'accept_num'    => Db::raw('accept_num+1'),
            'accept_executing_num'    => Db::raw('accept_executing_num+1'),
        ]);
        // 更新接任务统计
        (new AccountLevelLimit())->accept($limitDetails,$jobDetails['price'],time());

        return true;
    }


    /**
     * 关闭单个接单任务
     * @param $jobId 总任务ID
     * @param $jobVer 总任务版本
     * @param $isSuccess 接单任务是否为成功完成
     */
    public function orderTakeClose($jobId,$jobVer,$isSuccess) {
        $update['accept_executing_num']  = Db::raw('accept_executing_num-1');
        if($isSuccess) {
            $update['accept_finish_num']  = Db::raw('accept_finish_num+1');
        } else {
            $update['accept_fail_num']  = Db::raw('accept_fail_num+1');
        }
        //更新版本
        $this->updateByIdAndVer($jobId,$jobVer,$update);
        //检测任务情况
        $this->closeCheck($jobId,false);
        return true;
    }


    /**
     * 关闭任务
     * @param $jobDetails
     */
    private function close($jobDetails) {
        $count = WalletDetail::countRecordByJobId($jobDetails['id']);
        if(bccomp(array_sum($count),0,2) == 1) {
            throw new \Exception('金额发生错误，请联系管理员');
        }
        // 获取钱包详情
        $walletModel = new Wallet();
        if(!$walletDetails = $walletModel->findOne($jobDetails['publisher_id'])) {
            throw new \Exception('用户钱包账户不存在');
        }

        $amountPay = bcsub(0,$count['acceptIn'],2) - 0;

        // 解冻，并计算支出记录
        $walletModel->jobUnfrozen($walletDetails,$jobDetails['id'],abs($count['jobFrozen']),$amountPay);

        /************ 修改任务状态 ***********/
        $update['status'] = bccomp($amountPay,0,2) == 0?static::STATUS_FINISH_NOT_ACCEPT:static::STATUS_FINISH;
        $update['pay_amount'] = abs($amountPay);
        $this->updateByIdAndVer($jobDetails['id'],$jobDetails['ver'],$update);
        /********** END 修改任务状态 *********/

        return true;
    }

    /**
     * 检测并关闭任务
     * @param null $jobId 任务ID，不存在则检测所有任务
     * @param bool $startTrans 处理时是否单个开启事务
     * @param int $limit 每次执行的最大任务数
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function closeCheck($jobId = null,$startTrans = true,$limit = 500) {
        $query = clone $this;
        $where = [
            ['status','=',static::STATUS_EFFECTIVE],
            ['accept_executing_num','<=',0]
        ];
        if(!empty($jobId)) {
            array_unshift($where,['id','=',$jobId]);
        }
        $result = [
            'num'   => 0,
            'success'   => 0,
            'failed'    => 0,
            'failedMsgList' => [],  //执行错误的ID列表
        ];
       if(!$closeList = $query->where($where)->where(function($query){
           $query->where('exp_time','<',time())
           ->whereOr('accept_num','=',Db::Raw('num'));
       })->limit(0,$limit)->select()->toArray()) {
            return $result;
       }


        /**
         * 循环处理结单
         */
       foreach($closeList as $jobDetails) {
           if($startTrans) {
               Db::transaction(function () use(&$result,$jobDetails){
                   $result['num']++;
                   try {
                       $this->close($jobDetails);
                       $result['success']++;
                   } catch (\Exception $e) {
                       $result['failed']++;
                       $result['failedMsgList'][$jobDetails['id']] = $e->getMessage();
                   }
               });
           } else {
               $this->close($jobDetails);
               $result['num']++;
               $result['success']++;
           }
       }
       return $result;
    }


    /**
     * 获取任务详情
     * @param $id
     * @return array|null|\think\Model
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function findById($id,$userId = null) {
        $where = [
            'id'    => $id,
        ];
        if(!empty($userId)) {
            $where['publisher_id'] = $userId;
        }
        return $this->where($where)->find();
    }

    /**
     * 关联需求详情
     */
    public function requirement() {
        return $this->hasOne(Requirement::class,"job_id","id")->joinType("INNER")->field('requirement');
    }

    /**
     * 获取发布者
     * @return \think\model\relation\HasOne
     */
    public function publisher() {
        return $this->hasOne(\app\common\model\users\Users::class,"id","publisher_id")->joinType("INNER");

    }

    /**
     * 关联获取图片详情
     * @return \think\model\relation\HasMany
     */
    public function images() {
        return $this->hasMany(Association::class,'association_id','id')
            ->where('association_type','=',Association::ASSOCIATION_TYPE_LIST[static::FILE_TYPE])->where('status','=',Association::STATUS_EFFECTIVE)
            ->field('filename');
    }


    /**
     * 创建 发布记录
     * @param $data
     */
    private function issueRecord($data) {
        /********* 整理需要插入的数据 *********/
        $insertData = [];
        $fields = ['name','num','price','total_amount','type_id','task_link'];
        foreach($this->type as $key=>$type) {
            $value = '';
            switch($key) {
                case 'job_no':
                    $value = $this->getJobNo($data['time']);
                    break;
                case 'status':
                    $value = static::STATUS_PENDING_APPROVAL;   //待审批状态
                    break;
                case 'ver':
                    $value = 1;
                    break;
                case 'update_time':
                    $value = $data['time'];
                    break;
                case 'publisher_id':
                    $value = $data['user_id'];
                    break;
                case 'publish_time':
                    $value = $data['time'];
                    break;
                case 'exp_time':
                    $value =  $data['time'] + 60 * 60 * $data['duration'];
                    break;
                default:
                    switch($type) {
                        case 'integer':
                        case 'int':
                            $value = 0;
                            break;
                        case 'float':
                            $value = 0.0;
                            break;
                        case 'string':
                            break;

                    }
            }

            $insertData[$key] = $value;
        }
        foreach($fields as $field) {
            $insertData[$field] = array_key_exists($field,$data)?$data[$field]:'';
        }
        /******* END 整理需要插入的数据 *******/

        /******** 插入数据，并获取ID ******/
        $record = clone $this;
        $record->save($insertData);
        $jobId = $record->id;
        /****** END 插入数据，并获取ID ****/

        // 插入任务详情
        (new Requirement())->recordCreate([
            'job_id'    => $jobId,
            'requirement'   => $data['requirement'],
        ]);

        // 添加图片
        (new Association())->recordInsertListByJob($jobId,$data['verify_imgs']);

        // 返回任务详情
        return $record->toArray();
    }


    /**
     * 发布验证，并获取验证信息
     * @param $data post请求参数
     * @return array 返回验证信息数据
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function issueValidate($data) {
        $validateData = [
            'user_id'   => $this->getUserId(array_key_exists('user_id',$data)?$data['user_id']:null),
            'time'  => time(),
        ];

        // 获取并验证金额
        $walletModel = new Wallet();
        $validateData['wallet'] =  $walletModel->findOne($validateData['user_id']);
        if(bccomp($walletModel->getBalanceCount($validateData['wallet']),$data['balance'],2) !== 0) {
            throw new \Exception('你的金额已发生变化，请刷新重试');
        }

        // 获取并验证发布限制
        $time = $validateData['time'];
        $accountLevelimitModel = new AccountLevelLimit();
        $validateData['issueLimit'] = $accountLevelimitModel->findByUserId($validateData['user_id']);
        if($accountLevelimitModel->getIssueRemainTimes($validateData['issueLimit'],$time) < 1) {
            throw new \Exception('今天的发布次数已用完，请升级或明天再试！');
        }
        if($accountLevelimitModel->getIssueVerify($validateData['issueLimit']) != $data['verify']) {
            throw new \Exception('发布数据已更新，请刷新重试');
        }
        return $validateData;
    }


    /**
     * 获取用户ID
     * @param null $userId 用户ID
     * @return int|mixed|null
     * @throws \Exception
     */
    private function getUserId($userId = null) {
        if(!$userId = is_integer($userId)?$userId:Users::get('id')) {
            throw new \Exception('获取用户ID信息失败');
        }
        return $userId;
    }

    /**
     * 获取订单号
     * @param null $time
     * @return string
     */
    private function getJobNo($time = null){
        return date('YmdHis',empty($time)?time():$time) . rand(10000,99999);
    }


    /**
     * 通过ID和版本号更新数据
     * @param $userId
     * @param $version
     * @param $updateData
     */
    private function updateByIdAndVer($id,$version,$updateData)
    {

        $updateData['ver'] = Db::raw('ver+1');
        $updateData['update_time'] = time();
        if(!$this->where([
            'id'   => $id,
            'ver'   => $version,
        ])->update($updateData)) {
            throw new \Exception('数据发生变化，请刷新重试');
        }
        return true;
    }


    /** 获取关联所有数据的列表
     * @param array $whereTmp  条件
     * @param array $limit  查询数量
     * @param string $order 排序
     * @param array $fields 查询字段（暂缺）
     * @return array
     */
    public function recordAll($whereTmp = [],$limit = [],$orderTmp = [],$fieldsTmp= []) {
        return $this->recordAllRun([$whereTmp,$limit,$orderTmp,$fieldsTmp],[],[
            'requirement'      => [Requirement::getTable(),'requirement.job_id=' . static::getTable() . '.id','INNER'],
            'users'      => [\app\common\model\users\Users::getTable(),'users.id=' . static::getTable() . '.publisher_id','INNER'],
        ]);
    }

    //获取状态文本
    public static function getStatusText(int $status){
        return self::$status_text_mapping[$status] ?? '';
    }

    //获取状态列表
    public static function getStatusList()
    {
        return self::$status_text_mapping;
    }


    public function users() {
        return $this->hasOne(\app\common\model\users\Users::class,"id","publisher_id")->bind(['username'])->joinType("INNER");
    }

    public function jobType() {
        return $this->hasOne(\app\common\model\job\JobType::class,"id","type_id")->bind(['name' =>'type_name'])->joinType("INNER");
    }



    public function getList($condition,$size=10,$page=1){
        $count = $this->withJoin(["users","job_type"])->where($condition)->count();
        $data = $this->withJoin(["users","job_type","requirement"])->where($condition)->order('job.id','DESC')->paginate($size);
        return [
            "count"=>$count,
            "data"=>array_map([$this,'format'],$data->items())
        ];
    }


    public function getStatusTextAttr($value,$data)
    {
        return static::getStatusText($data['status']) ?? '';
    }

/*    public function getPublishTimeAttr($value){
       return date("Y-m-d H:i:s",$value);
    }

    public function getExpTimeAttr($value){
       return date("Y-m-d H:i:s",$value);
    } */

    public function getPublishTimeTextAttr($value,$data)
    {
       return date("Y-m-d H:i:s",$data['publish_time']);
    }

    public function getExpTimeTextAttr($value,$data)
    {
       return date("Y-m-d H:i:s",$data['exp_time']);
    }

    protected function format($value)
    {
        $value->status_name=$value->status_text;
        $value->type_name= $value->type_name ?? $value->job_type->name ?? $value->jobType__name;
        $value->publisher=$value->username;
        $value->publish_time_text=$value->publish_time_text;
        $value->exp_time_text=$value->exp_time_text;
        return $value;
    }

}
